﻿using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Appology.SugarSync.IO;

namespace Appology.SugarSync.Explorer.Desktop
{
    public partial class MainForm : Form
    {
        private static SugarSyncAccount _sugarSyncAccount;
        private static SugarSyncStorageRoot _sugarSyncStorageRoot;

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
        }

        private async void LoginButton_Click(object sender, EventArgs e)
        {
            Explorer.Nodes.Clear();
            Files.Items.Clear();

            _sugarSyncAccount = new SugarSyncAccount();
            _sugarSyncStorageRoot = new SugarSyncStorageRoot("sugar sync account", _sugarSyncAccount);

            _sugarSyncAccount.SetApiCredentials("<put your access key here>", "<put your private access key here>");
            
            _sugarSyncAccount.SetUserCredentials(Username.Text, Password.Text);

            ConnectionStatus.Text = "Authenticating...";

            if (await _sugarSyncAccount.AuthenticateAsync())
            {
                ConnectionStatus.Text = "Refreshing...";

                await _sugarSyncAccount.RefreshAsync();

                FreeSpace.Text = string.Format("{0} MB free of {1} MB total", _sugarSyncAccount.FreeBytes / 1024 / 1024, _sugarSyncAccount.TotalBytes / 1024 / 1024);

                ConnectionStatus.Text = "Getting Directory Information...";

                await _sugarSyncStorageRoot.RefreshAsync();

                foreach (var sugarSyncDirectoryInfo in _sugarSyncStorageRoot.Contents.OfType<SugarSyncDirectoryInfo>())
                {
                    AddDirectoryInfoToExplorer(sugarSyncDirectoryInfo);
                }

                ConnectionStatus.Text = "Done.";
            }
            else
            {
                MessageBox.Show("Invalid username and/or password.");
            }
        }

        private void AddDirectoryInfoToExplorer(SugarSyncDirectoryInfo directoryInfo)
        {
            var node = new TreeNode(directoryInfo.Name);
            node.Tag = directoryInfo;

            Explorer.Nodes.Add(node);
        }

        private void Explorer_AfterSelect(object sender, TreeViewEventArgs e)
        {
            UpdateSelectedExplorerNode();
        }

        private async void UpdateSelectedExplorerNode()
        {
            if (Explorer.SelectedNode != null)
            {
                TreeNode selectedNode = Explorer.SelectedNode;
                var selectedDirectoryInfo = (SugarSyncDirectoryInfo)selectedNode.Tag;

                selectedNode.Nodes.Clear();
                Files.Items.Clear();

                await selectedDirectoryInfo.RefreshAsync();

                foreach (SugarSyncDirectoryInfo directoryInfo in selectedDirectoryInfo.Contents.OfType<SugarSyncDirectoryInfo>())
                {
                    var node = new TreeNode(directoryInfo.Name);
                    node.Tag = directoryInfo;

                    selectedNode.Nodes.Add(node);
                }

                foreach (SugarSyncFileInfo fileInfo in selectedDirectoryInfo.Contents.OfType<SugarSyncFileInfo>())
                {
                    SugarSyncFileInfo info = fileInfo;

                    await fileInfo.RefreshAsync();

                    var listViewItem = new ListViewItem(info.Name);
                    listViewItem.SubItems.Add(info.Length.ToString());
                    listViewItem.SubItems.Add(info.ContentType);
                    listViewItem.SubItems.Add(info.DateCreated.ToString());
                    listViewItem.SubItems.Add(info.DateModified.ToString());
                    listViewItem.Tag = info;
                    Files.Items.Add(listViewItem);
                }

                selectedNode.Expand();
            }
        }

        private async void createFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var directoryInfo = (SugarSyncDirectoryInfo)Explorer.SelectedNode.Tag;
            var newFolder = Microsoft.VisualBasic.Interaction.InputBox("New Folder Name", "Create Folder", "New Folder");
            await directoryInfo.CreateDirectoryInfo(newFolder);
            UpdateSelectedExplorerNode();
        }

        private async void deleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var directoryInfo = (SugarSyncDirectoryInfo)Explorer.SelectedNode.Tag;
            await directoryInfo.Delete();
            var updateSelectedExplorerNodeParentDelegate = new UpdateSelectedExplorerNodeDelegate(UpdateSelectedExplorerNodeParent);
            Invoke(updateSelectedExplorerNodeParentDelegate);
        }

        private async void renameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var directoryInfo = (SugarSyncDirectoryInfo)Explorer.SelectedNode.Tag;
            var newFolderName = "Test Folder 2";
            await directoryInfo.UpdateInfo(newFolderName);
            UpdateSelectedExplorerNode();
            Explorer.SelectedNode.Text = newFolderName;
        }

        private async void refreshToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var directoryInfo = (SugarSyncDirectoryInfo)Explorer.SelectedNode.Tag;
            await directoryInfo.RefreshAsync();
            UpdateSelectedExplorerNode();
        }

        private async void deleteFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileInfo = (SugarSyncFileInfo) Files.SelectedItems[0].Tag;
            await fileInfo.Delete();
            var updateSelectedExplorerNodeDelegate = new UpdateSelectedExplorerNodeDelegate(UpdateSelectedExplorerNode);
            Invoke(updateSelectedExplorerNodeDelegate);
        }

        private delegate void UpdateSelectedExplorerNodeParentDelegate();
        private void UpdateSelectedExplorerNodeParent()
        {
            Files.Items.Clear();
            Explorer.SelectedNode = Explorer.SelectedNode.Parent;
        }
        private delegate void UpdateSelectedExplorerNodeDelegate();

        private async void renameFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileInfoListViewItem = Files.SelectedItems[0];
            var fileInfo = (SugarSyncFileInfo)fileInfoListViewItem.Tag;
            var newName = "New Name";
            await fileInfo.UpdateInfo(newName, fileInfo.ContentType);
            fileInfoListViewItem.Text = newName;
        }

        private async void refreshFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var fileInfoListViewItem = Files.SelectedItems[0];
            var fileInfo = (SugarSyncFileInfo)fileInfoListViewItem.Tag;
            await fileInfo.RefreshAsync();
            UpdateSelectedExplorerNode();
        }

        private async void uploadFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var directoryInfo = (SugarSyncDirectoryInfo) Explorer.SelectedNode.Tag;

            var ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                string contentType = string.Empty;

                if(ofd.FileName.EndsWith(".txt", StringComparison.InvariantCultureIgnoreCase))
                {
                    contentType = "text/plain";
                }
                else if (ofd.FileName.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
                {
                    contentType = "image/png";
                }
                else
                {
                    contentType = "application/octet-stream";
                }

                var fileInfo = await directoryInfo.CreateFileInfo(Path.GetFileName(ofd.FileName), contentType);

                ConnectionStatus.Text = "Opening...";

                using (var sw = await fileInfo.OpenWriteAsync())
                {
                    ConnectionStatus.Text = "Uploading...";

                    var transferContext = new TransferContext();

                    transferContext.Target = sw;
                    transferContext.Source = ofd.OpenFile();

                    transferContext.OnComplete = () =>
                    {
                        var refreshFileInfoDelegate = new RefreshFileInfoDelegate(RefreshFileInfo);
                        Invoke(refreshFileInfoDelegate, fileInfo);
                    };

                    var transferThread = new Thread(TransferFile);
                    transferThread.Start(transferContext);
                }
            }
        }

        private delegate void RefreshFileInfoDelegate(SugarSyncFileInfo fileInfo);

        private async void RefreshFileInfo(SugarSyncFileInfo fileInfo)
        {
            await fileInfo.RefreshAsync();

            var listViewItem = new ListViewItem(fileInfo.Name);
            listViewItem.SubItems.Add(fileInfo.Length.ToString());
            listViewItem.SubItems.Add(fileInfo.ContentType);
            listViewItem.SubItems.Add(fileInfo.DateCreated.ToString());
            listViewItem.SubItems.Add(fileInfo.DateModified.ToString());
            listViewItem.Tag = fileInfo;
            Files.Items.Add(listViewItem);
        }

        private delegate void UpdateConnectionStatusDelegate(string statusText);

        private void UpdateConnectionStatus(string statusText)
        {
            ConnectionStatus.Text = statusText;
        }

        private async void getFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if(Files.SelectedItems.Count == 0)
            {
                return;
            }

            var fileInfoListViewItem = Files.SelectedItems[0];
            var fileInfo = (SugarSyncFileInfo)fileInfoListViewItem.Tag;
            var sfd = new SaveFileDialog();

            sfd.FileName = fileInfo.Name;

            if(sfd.ShowDialog() == DialogResult.OK)
            {
                ConnectionStatus.Text = "Opening...";

                var sr = await fileInfo.OpenReadAsync();

                ConnectionStatus.Text = "Downloading...";

                var transferContext = new TransferContext();

                transferContext.Target = sfd.OpenFile();
                transferContext.Source = sr;

                var transferThread = new Thread(TransferFile);
                transferThread.Start(transferContext);
            }
        }

        private void TransferFile(object parameter)
        {
            var transferContext = (TransferContext) parameter;

            var ucs = new UpdateConnectionStatusDelegate(UpdateConnectionStatus);

            int receivedBytes = 0;
            var buffer = new byte[65536];

            int bytesRead;

            do
            {
                bytesRead = transferContext.Source.Read(buffer, 0, buffer.Length);
                receivedBytes += bytesRead;
                
                Invoke(ucs, string.Format("Transferred {0} bytes...", receivedBytes));
                transferContext.Target.Write(buffer, 0, bytesRead);
                
            } while (bytesRead > 0);

            transferContext.Target.Flush();
            transferContext.Target.Close();

            transferContext.Source.Close();

            Invoke(ucs, string.Format("Transferred {0} bytes...Done.", receivedBytes));

            if(transferContext.OnComplete != null)
            {
                transferContext.OnComplete();
            }
        }

        private void FilesContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            bool isEnabled = Files.SelectedItems.Count > 0;

            getFileToolStripMenuItem.Enabled = isEnabled;
            deleteFileToolStripMenuItem.Enabled = isEnabled;
            renameFileToolStripMenuItem.Enabled = isEnabled;
        }

        private void ExplorerContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // should include check for root folder...
            bool isEnabled = Explorer.SelectedNode != null && Explorer.SelectedNode.Tag is SugarSyncDirectoryInfo;

            createFolderToolStripMenuItem.Enabled = isEnabled;
            uploadFileToolStripMenuItem.Enabled = isEnabled;
            deleteToolStripMenuItem.Enabled = isEnabled;
            renameToolStripMenuItem.Enabled = isEnabled;
        }
    }

    public class TransferContext
    {
        public Stream Target { get; set; }
        public Stream Source { get; set; }
        public Action OnComplete { get; set; }
    }
}